﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Sofire.Expressions.Domain
{


    public class EvaluationVisitor : LogicalExpressionVisitor
    {
        private delegate T Func<T>();

        private readonly EvaluateOptions _options = EvaluateOptions.None;

        private bool IgnoreCase { get { return (_options & EvaluateOptions.IgnoreCase) == EvaluateOptions.IgnoreCase; } }

        public EvaluationVisitor(EvaluateOptions options)
        {
            _options = options;
        }

        public object Result { get; private set; }

        private object Evaluate(LogicalExpression expression)
        {
            expression.Accept(this);
            return Result;
        }

        public override void Visit(LogicalExpression expression)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        private static Type[] CommonTypes = new[] { typeof(Int64), typeof(Double), typeof(Boolean), typeof(String), typeof(Decimal) };

        /// <summary>
        /// Gets the the most precise type.
        /// </summary>
        /// <param name="a">Type a.</param>
        /// <param name="b">Type b.</param>
        /// <returns></returns>
        private static Type GetMostPreciseType(Type a, Type b)
        {
            foreach(Type t in CommonTypes)
            {
                if(a == t || b == t)
                {
                    return t;
                }
            }

            return a;
        }

        public int CompareUsingMostPreciseType(object a, object b)
        {
            if(a == b || object.Equals(a, b)) return 0;
            if(a == null) return -1;
            if(b == null) return 1;
            Type mpt = GetMostPreciseType(a.GetType(), b.GetType());
            return Comparer.Default.Compare(Convert.ChangeType(a, mpt), Convert.ChangeType(b, mpt));
        }

        public override void Visit(TernaryExpression expression)
        {
            // Evaluates the left expression and saves the value
            expression.LeftExpression.Accept(this);
            bool left = Convert.ToBoolean(Result);

            if(left)
            {
                expression.MiddleExpression.Accept(this);
            }
            else
            {
                expression.RightExpression.Accept(this);
            }
        }

        private static bool IsReal(object value)
        {
            var typeCode = Type.GetTypeCode(value.GetType());

            return typeCode == TypeCode.Decimal || typeCode == TypeCode.Double || typeCode == TypeCode.Single;
        }

        public override void Visit(BinaryExpression expression)
        {
            // simulate Lazy<Func<>> behavior for late evaluation
            object leftValue = null;
            Func<object> left = () =>
                                 {
                                     if(leftValue == null)
                                     {
                                         expression.LeftExpression.Accept(this);
                                         leftValue = Result;
                                     }
                                     return leftValue;
                                 };

            // simulate Lazy<Func<>> behavior for late evaluation
            object rightValue = null;
            Func<object> right = () =>
            {
                if(rightValue == null)
                {
                    expression.RightExpression.Accept(this);
                    rightValue = Result;
                }
                return rightValue;
            };

            switch(expression.Type)
            {
                case BinaryExpressionType.And:
                    Result = Convert.ToBoolean(left()) && Convert.ToBoolean(right());
                    break;

                case BinaryExpressionType.Or:
                    Result = Convert.ToBoolean(left()) || Convert.ToBoolean(right());
                    break;

                case BinaryExpressionType.Div:
                    Result = IsReal(left()) || IsReal(right())
                                 ? Numbers.Divide(left(), right())
                                 : Numbers.Divide(Convert.ToDouble(left()), right());
                    break;

                case BinaryExpressionType.Equal:
                    // Use the type of the left operand to make the comparison
                    Result = CompareUsingMostPreciseType(left(), right()) == 0;
                    break;

                case BinaryExpressionType.Greater:
                    // Use the type of the left operand to make the comparison
                    Result = CompareUsingMostPreciseType(left(), right()) > 0;
                    break;

                case BinaryExpressionType.GreaterOrEqual:
                    // Use the type of the left operand to make the comparison
                    Result = CompareUsingMostPreciseType(left(), right()) >= 0;
                    break;

                case BinaryExpressionType.Lesser:
                    // Use the type of the left operand to make the comparison
                    Result = CompareUsingMostPreciseType(left(), right()) < 0;
                    break;

                case BinaryExpressionType.LesserOrEqual:
                    // Use the type of the left operand to make the comparison
                    Result = CompareUsingMostPreciseType(left(), right()) <= 0;
                    break;

                case BinaryExpressionType.Minus:
                    Result = Numbers.Soustract(left(), right());
                    break;

                case BinaryExpressionType.Modulo:
                    Result = Numbers.Modulo(left(), right());
                    break;

                case BinaryExpressionType.NotEqual:
                    // Use the type of the left operand to make the comparison
                    Result = CompareUsingMostPreciseType(left(), right()) != 0;
                    break;

                case BinaryExpressionType.Plus:
                    if(left() is string)
                    {
                        Result = String.Concat(left(), right());
                    }
                    else
                    {
                        Result = Numbers.Add(left(), right());
                    }

                    break;

                case BinaryExpressionType.Times:
                    Result = Numbers.Multiply(left(), right());
                    break;

                case BinaryExpressionType.BitwiseAnd:
                    Result = Convert.ToUInt16(left()) & Convert.ToUInt16(right());
                    break;


                case BinaryExpressionType.BitwiseOr:
                    Result = Convert.ToUInt16(left()) | Convert.ToUInt16(right());
                    break;


                case BinaryExpressionType.BitwiseXOr:
                    Result = Convert.ToUInt16(left()) ^ Convert.ToUInt16(right());
                    break;


                case BinaryExpressionType.LeftShift:
                    Result = Convert.ToUInt16(left()) << Convert.ToUInt16(right());
                    break;


                case BinaryExpressionType.RightShift:
                    Result = Convert.ToUInt16(left()) >> Convert.ToUInt16(right());
                    break;
            }
        }

        public override void Visit(UnaryExpression expression)
        {
            // Recursively evaluates the underlying expression
            expression.Expression.Accept(this);

            switch(expression.Type)
            {
                case UnaryExpressionType.Not:
                    Result = !Convert.ToBoolean(Result);
                    break;

                case UnaryExpressionType.Negate:
                    Result = Numbers.Soustract(0, Result);
                    break;

                case UnaryExpressionType.BitwiseNot:
                    Result = ~Convert.ToUInt16(Result);
                    break;
            }
        }

        public override void Visit(ValueExpression expression)
        {
            Result = expression.Value;
        }

        #region Function Visit

        private bool StringFunction(Function function, out string value)
        {
            object testValue = Evaluate(function.Expressions[0]);
            if(testValue == null)
            {
                goto END;
            }
            if(testValue is string)
            {
                value = ((string)testValue);
                return true;
            }
            function.Throw("方法的参数必须是一个字符串类型。");
        END:
            value = null;
            return false;
        }

        private bool StringFunction(Function function, string checkName, int paramCount, out string value)
        {
            CheckCase(checkName, function.Identifier.Name);

            function.TryThrowCount(paramCount);

            return StringFunction(function, out value);

        }

        private void StringTrim(Function function, string checkName, Func<string, char[], string> func)
        {
            CheckCase(checkName, function.Identifier.Name);

            var length = function.TryThrowCount(1, 2);

            string value;
            if(this.StringFunction(function, out value))
            {
                char[] c = null;
                if(length > 1)
                {
                    var p2 = GetString(function, 1);
                    c = p2.ToCharArray();
                }
                Result = func(value, c);
            }
            else Result = null;

        }

        private void StringContains(Function function, string checkName, Func<string, string, bool> func)
        {
            string value;
            if(this.StringFunction(function, checkName, 2, out value))
            {
                var p2 = GetString(function, 1);
                Result = func(value, p2);
            }
            else Result = false;
        }

        private bool IsNumber(object obj)
        {
            return obj is Int32 || obj is Double || obj is Decimal || obj is Single
                || obj is Int64 || obj is Int16
                || obj is UInt16 || obj is UInt32 || obj is UInt64
                || obj is Byte || obj is SByte;
        }

        private void StringIndexOf(Function function, string checkName, Func<string, string, int, int, int> func)
        {
            CheckCase(checkName, function.Identifier.Name);

            var length = function.TryThrowCount(2, 4);

            string value;
            if(this.StringFunction(function, out value))
            {
                var p2 = GetString(function, 1);

                int startIndex = -1;
                int count = -1;

                if(length > 2)
                {
                    startIndex = GetInteger(function, 2);

                    if(length > 3)
                    {
                        count = GetInteger(function, 3);
                    }

                }
                Result = func(value, p2, startIndex, count);
            }
            else Result = -1;
        }

        private bool StringIndexAndLength(Function function, string checkName, out string value, out int startIndex, out int length)
        {
            CheckCase(checkName, function.Identifier.Name);
            var pLength = function.TryThrowCount(2, 3);

            if(this.StringFunction(function, out value))
            {
                startIndex = GetInteger(function, 1);
                length = value.Length - startIndex;

                if(pLength > 2)
                {
                    length = GetInteger(function, 2);
                }
                return true;
            }

            startIndex = 0;
            length = 0;
            return false;
        }

        private RegexOptions CreateRegexOptions(string str)
        {
            //mir
            RegexOptions options = RegexOptions.None;
            foreach(var c in str)
            {
                switch(c)
                {
                    case 'M':
                    case 'm':
                        options |= RegexOptions.Multiline;
                        break;
                    case 'I':
                    case 'i':
                        options |= RegexOptions.IgnoreCase;
                        break;
                    case 'R':
                    case 'r':
                        options |= RegexOptions.RightToLeft;
                        break;
                    default:

                        break;
                }
            }
            return options;
        }

        private void RegexFunction(Function function, string checkName, Func<string, string, RegexOptions, object> func)
        {
            CheckCase(checkName, function.Identifier.Name);
            var length = function.TryThrowCount(2, 3);

            var input = GetString(function, 0);
            var pattern = GetString(function, 1);

            RegexOptions options = RegexOptions.None;
            if(length > 2)
            {
                options = this.CreateRegexOptions(GetString(function, 2));
            }

            Result = func(input, pattern, options);
        }

        private void GetDateInfo(Function function, string checkName, Func<DateTime, int> func)
        {
            CheckCase(checkName, function.Identifier.Name);
            function.TryThrowCount(1);
            var date = this.GetDate(function, 0);
            Result = func((DateTime)date);
        }

        public override void Visit(Function function)
        {
            #region Core

            var args = new FunctionArgs
                           {
                               Parameters = new Expression[function.Expressions.Length]
                           };

            // Don't call parameters right now, instead let the function do it as needed.
            // Some parameters shouldn't be called, for instance, in a if(), the "not" value might be a division by zero
            // Evaluating every value could produce unexpected behaviour
            for(int i = 0 ; i < function.Expressions.Length ; i++)
            {
                args.Parameters[i] = new Expression(function.Expressions[i], _options);
                args.Parameters[i].EvaluateFunction += EvaluateFunction;
                args.Parameters[i].EvaluateParameter += EvaluateParameter;

                // Assign the parameters of the Expression to the arguments so that custom Functions and Parameters can use them
                args.Parameters[i].Parameters = Parameters;
                args.Parameters[i].Functions = Functions;
            }
            var lowerName = IgnoreCase ? function.Identifier.Name.ToLower() : function.Identifier.Name;
            if(this.Functions != null)
            {
                UserFunction eventHandler;
                if(this.Functions.TryGetValue(function.Identifier.Name, out eventHandler))
                {
                    Result = eventHandler(new UserFunctionArguments(args));
                    return;
                }
            }
            // Calls external implementation
            OnEvaluateFunction(IgnoreCase ? function.Identifier.Name.ToLower() : function.Identifier.Name, args);

            // If an external implementation was found get the result back
            if(args.HasResult)
            {
                Result = args.Result;
                return;
            }

            #endregion

            switch(function.Identifier.Name.ToLower())
            {
                #region 数学函数

                #region Abs
                case "abs":

                    CheckCase("Abs", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Abs(Convert.ToDecimal(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Acos
                case "acos":

                    CheckCase("Acos", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Acos(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Asin
                case "asin":

                    CheckCase("Asin", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Asin(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Atan
                case "atan":

                    CheckCase("Atan", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Atan(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Ceiling
                case "ceiling":

                    CheckCase("Ceiling", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Ceiling(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Cos

                case "cos":

                    CheckCase("Cos", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Cos(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Exp
                case "exp":

                    CheckCase("Exp", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Exp(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Floor
                case "floor":

                    CheckCase("Floor", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Floor(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region IEEERemainder
                case "ieeeremainder":

                    CheckCase("IEEERemainder", function.Identifier.Name);

                    function.TryThrowCount(2);

                    Result = Math.IEEERemainder(Convert.ToDouble(Evaluate(function.Expressions[0])), Convert.ToDouble(Evaluate(function.Expressions[1])));

                    break;

                #endregion

                #region Log
                case "log":

                    CheckCase("Log", function.Identifier.Name);

                    function.TryThrowCount(2);

                    Result = Math.Log(Convert.ToDouble(Evaluate(function.Expressions[0])), Convert.ToDouble(Evaluate(function.Expressions[1])));

                    break;

                #endregion

                #region Log10
                case "log10":

                    CheckCase("Log10", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Log10(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Pow
                case "pow":

                    CheckCase("Pow", function.Identifier.Name);

                    function.TryThrowCount(2);

                    Result = Math.Pow(Convert.ToDouble(Evaluate(function.Expressions[0])), Convert.ToDouble(Evaluate(function.Expressions[1])));

                    break;

                #endregion

                #region Round
                case "round":

                    CheckCase("Round", function.Identifier.Name);

                    function.TryThrowCount(2);

                    MidpointRounding rounding = (_options & EvaluateOptions.RoundAwayFromZero) == EvaluateOptions.RoundAwayFromZero ? MidpointRounding.AwayFromZero : MidpointRounding.ToEven;

                    Result = Math.Round(Convert.ToDouble(Evaluate(function.Expressions[0])), Convert.ToInt16(Evaluate(function.Expressions[1])), rounding);

                    break;

                #endregion

                #region Sign
                case "sign":

                    CheckCase("Sign", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Sign(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Sin
                case "sin":

                    CheckCase("Sin", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Sin(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Sqrt
                case "sqrt":

                    CheckCase("Sqrt", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Sqrt(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Tan
                case "tan":

                    CheckCase("Tan", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Tan(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Truncate
                case "truncate":

                    CheckCase("Truncate", function.Identifier.Name);

                    function.TryThrowCount(1);

                    Result = Math.Truncate(Convert.ToDouble(Evaluate(function.Expressions[0])));

                    break;

                #endregion

                #region Max
                case "max":

                    CheckCase("Max", function.Identifier.Name);

                    function.TryThrowCount(2);

                    object maxleft = Evaluate(function.Expressions[0]);
                    object maxright = Evaluate(function.Expressions[1]);

                    Result = Numbers.Max(maxleft, maxright);
                    break;

                #endregion

                #region Min
                case "min":

                    CheckCase("Min", function.Identifier.Name);

                    function.TryThrowCount(2);

                    object minleft = Evaluate(function.Expressions[0]);
                    object minright = Evaluate(function.Expressions[1]);

                    Result = Numbers.Min(minleft, minright);
                    break;

                #endregion

                #endregion

                #region 字符串函数

                #region IsEmpty

                case "isempty":
                    {
                        string value;
                        if(this.StringFunction(function, "isEmpty", 1, out value))
                        {
                            Result = string.IsNullOrEmpty(value);
                        }
                        else Result = true;
                        break;
                    }
                #endregion

                #region len

                case "len":
                    {
                        CheckCase("len", function.Identifier.Name);
                        function.TryThrowCount(1);
                        object testValue = Evaluate(function.Expressions[0]);
                        if(testValue == null)
                        {
                            Result = 0;
                        }
                        else if(testValue is string)
                        {
                            Result = Convert.ToString(testValue).Length;
                        }
                        else if(testValue is Array)
                        {
                            Result = (testValue as Array).Length;
                        }
                        else if(testValue is ICollection)
                        {
                            Result = (testValue as ICollection).Count;
                        }
                        else if(testValue is IEnumerable)
                        {
                            int index = 0;
                            foreach(var item in (testValue as IEnumerable))
                            {
                                index++;
                            }
                            Result = index;
                        }
                        else function.Throw("方法的参数必须是一个字符串、数组、集合或可枚举的类型。");
                        break;
                    }


                #endregion

                #region lower

                case "lower":
                    {
                        string value;
                        if(this.StringFunction(function, "lower", 1, out value))
                        {
                            Result = value.ToLower();
                        }
                        else Result = null;
                        break;
                    }

                #endregion

                #region upper

                case "upper":
                    {
                        string value;
                        if(this.StringFunction(function, "upper", 1, out value))
                        {
                            Result = value.ToUpper();
                        }
                        else Result = null;
                        break;
                    }

                #endregion

                #region trim

                case "trim":
                    this.StringTrim(function, "trim", (value, c) => value.Trim(c));
                    break;

                case "ltrim":
                    this.StringTrim(function, "ltrim", (value, c) => value.TrimStart(c));
                    break;

                case "rtrim":
                    this.StringTrim(function, "rtrim", (value, c) => value.TrimEnd(c));
                    break;

                #endregion

                #region contains

                case "contains":
                    {
                        this.StringContains(function, "contains", (value, p2) => value.IndexOf(p2, StringComparison.CurrentCultureIgnoreCase) > -1);
                        break;
                    }

                case "left":
                    {
                        this.StringContains(function, "left", (value, p2) => value.StartsWith(p2, StringComparison.CurrentCultureIgnoreCase));
                        break;
                    }
                case "right":
                    {
                        this.StringContains(function, "right", (value, p2) => value.EndsWith(p2, StringComparison.CurrentCultureIgnoreCase));
                        break;
                    }

                #endregion

                #region indexOf

                case "indexof":

                    this.StringIndexOf(function, "indexOf", (str, value, startIndex, count) =>
                    {
                        if(startIndex == -1) startIndex = 0;
                        if(count == -1) count = str.Length - startIndex;
                        return str.IndexOf(value, startIndex, count, StringComparison.CurrentCultureIgnoreCase);
                    });
                    break;

                case "lastindexof":

                    this.StringIndexOf(function, "lastIndexOf", (str, value, startIndex, count) =>
                    {
                        if(startIndex == -1) startIndex = str.Length - 1;
                        if(count == -1) count = startIndex + 1;
                        return str.LastIndexOf(value, startIndex, count, StringComparison.CurrentCultureIgnoreCase);
                    });
                    break;
                #endregion

                #region substr

                case "substr":
                    {
                        string value;
                        int startIndex, length;
                        if(StringIndexAndLength(function, "substr", out value, out startIndex, out length))
                        {
                            Result = value.Substring(startIndex, length);
                        }
                        else Result = null;
                        break;
                    }
                case "remove":
                    {
                        string value;
                        int startIndex, length;
                        if(StringIndexAndLength(function, "remove", out value, out startIndex, out length))
                        {
                            Result = value.Remove(startIndex, length);
                        }
                        else Result = null;
                        break;
                    }
                #endregion

                #region replace

                case "replace":
                    {
                        CheckCase("replace", function.Identifier.Name);
                        function.TryThrowCount(3);

                        var p1 = Evaluate(function.Expressions[0]) as string;
                        if(p1 == null) function.Throw("方法的第一个参数仅允许为字符串类型。");
                        var p2 = Evaluate(function.Expressions[1]) as string;
                        if(p2 == null) function.Throw("方法的第二个参数仅允许为字符串类型。");
                        var p3 = Evaluate(function.Expressions[2]) as string;
                        if(p3 == null) function.Throw("方法的第三个参数仅允许为字符串类型。");

                        Result = p1.Replace(p2, p3);
                        break;
                    }

                #endregion

                #region Regex

                case "regexmatch":
                    {
                        this.RegexFunction(function, "regexMatch", (input, pattern, options) => Regex.IsMatch(input, pattern, options));
                        break;
                    }
                case "regexsplit":
                    {
                        this.RegexFunction(function, "regexSplit", (input, pattern, options) => Regex.Split(input, pattern, options));
                        break;
                    }

                case "regexreplace":
                    {
                        CheckCase("regexReplace", function.Identifier.Name);
                        var length = function.TryThrowCount(3, 4);

                        var input = Evaluate(function.Expressions[0]) as string;
                        if(input == null) function.Throw("方法的第一个参数仅允许为字符串类型。");
                        var pattern = Evaluate(function.Expressions[1]) as string;
                        if(pattern == null) function.Throw("方法的第二个参数仅允许为字符串类型。");
                        var replacement = Evaluate(function.Expressions[2]) as string;
                        if(replacement == null) function.Throw("方法的第三个参数仅允许为字符串类型。");

                        RegexOptions options = RegexOptions.None;
                        if(length > 3)
                        {
                            var p4 = Evaluate(function.Expressions[3]) as string;
                            if(p4 == null) function.Throw("方法的第四个参数仅允许为字符串类型。");
                            options = this.CreateRegexOptions(p4);
                        }
                        Result = Regex.Replace(input, pattern, replacement, options);
                        break;
                    }

                #endregion

                #region Concat

                case "concat":
                    {
                        CheckCase("concat", function.Identifier.Name);
                        var length = function.TryThrowMinCount(1);
                        System.Text.StringBuilder builder = new System.Text.StringBuilder();
                        for(int i = 0 ; i < length ; i++)
                        {
                            builder.Append(Evaluate(function.Expressions[i]));
                        }
                        Result = builder.ToString();
                        break;
                    }
                #endregion

                #endregion

                #region 日期函数

                #region date

                case "date":
                    {
                        CheckCase("date", function.Identifier.Name);
                        var length = function.Expressions.Length;
                        if(length == 0)
                        {
                            Result = DateTime.Now;
                            break;
                        }
                        if(length == 1)
                        {
                            var ticks = GetLong(function, 0);
                            Result = new DateTime(ticks);
                            break;
                        }
                        if(length != 3 && length != 6 && length != 7)
                            function.Throw("方法仅允许 0、1、3、6 或 7 个参数（实际提供了 " + length + " 个参数）。");

                        int year, month, day
                            , hour = 0, minute = 0, second = 0, millisecond = 0;

                        year = GetInteger(function, 0);
                        month = GetInteger(function, 1);
                        day = GetInteger(function, 2);
                        if(length == 6)
                        {
                            hour = GetInteger(function, 3);
                            minute = GetInteger(function, 4);
                            second = GetInteger(function, 5);
                        }

                        if(length == 7) millisecond = GetInteger(function, 6);

                        Result = new DateTime(year, month, day, hour, minute, second, millisecond);

                        break;
                    }

                #endregion

                #region GetDateInfo

                case "year":
                    {
                        this.GetDateInfo(function, "year", (date) => date.Year);
                        break;
                    }
                case "month":
                    {
                        this.GetDateInfo(function, "month", (date) => date.Month);
                        break;
                    }
                case "day":
                    {
                        this.GetDateInfo(function, "day", (date) => date.Day);
                        break;
                    }
                case "hour":
                    {
                        this.GetDateInfo(function, "hour", (date) => date.Hour);
                        break;
                    }
                case "minute":
                    {
                        this.GetDateInfo(function, "minute", (date) => date.Minute);
                        break;
                    }
                case "second":
                    {
                        this.GetDateInfo(function, "second", (date) => date.Second);
                        break;
                    }
                case "millisecond":
                    {
                        this.GetDateInfo(function, "millisecond", (date) => date.Millisecond);
                        break;
                    }
                case "dayofweek":
                    {
                        this.GetDateInfo(function, "dayOfWeek", (date) => (int)date.DayOfWeek);
                        break;
                    }

                #endregion

                #region datein/dateadd

                case "datein":
                    {
                        CheckCase("dateIn", function.Identifier.Name);
                        function.TryThrowCount(3);

                        var date = GetDate(function, 0);
                        var begin = GetDate(function, 1);
                        var end = GetDate(function, 2);

                        Result = date >= begin && date <= end;

                        break;
                    }
                case "dateadd":
                    {
                        CheckCase("dateAdd", function.Identifier.Name);
                        function.TryThrowCount(2);

                        var date = GetDate(function, 0);
                        var addDays = GetFloat(function, 1);
                        Result = date.AddDays(addDays);
                        break;
                    }

                #endregion

                #endregion

                #region 转换函数

                case "todate":
                    {
                        CheckCase("toDate", function.Identifier.Name);
                        var length = function.TryThrowCount(1);
                        var obj = Evaluate(function.Expressions[0]);
                        if(obj == null) function.Throw("方法的第一个参数不得为空！");

                        if(obj is DateTime)
                        {
                            Result = (DateTime)obj;
                        }
                        else if(obj is string)
                        {
                            Result = DateTime.Parse((string)obj);
                        }
                        else if(this.IsNumber(obj))
                        {
                            Result = new DateTime((long)obj);
                        }
                        else
                        {
                            function.Throw("方法的第一个参数不是字符串、数字或者日期类型");
                        }

                        break;
                    }

                case "tointeger":
                    {
                        CheckCase("toInteger", function.Identifier.Name);
                        var length = function.TryThrowCount(1);
                        var obj = Evaluate(function.Expressions[0]);

                        if(this.IsNumber(obj))
                        {
                            Result = Convert.ToInt32(obj);
                        }
                        else
                        {
                            Result = int.Parse(Convert.ToString(obj));
                        }
                        break;
                    }
                case "tofloat":
                    {
                        CheckCase("toFloat", function.Identifier.Name);
                        var length = function.TryThrowCount(1);
                        var obj = Evaluate(function.Expressions[0]);
                        if(this.IsNumber(obj))
                        {
                            Result = Convert.ToDouble(obj);
                        }
                        else
                        {
                            Result = Double.Parse(Convert.ToString(obj));
                        }
                        break;
                    }
                case "toboolean":
                    {
                        CheckCase("toBoolean", function.Identifier.Name);
                        var length = function.TryThrowCount(1);
                        var bs = GetString(function, 0);

                        Result = !(bs == "false"
                            || bs == "0"
                            || bs == "no"
                            || bs == "off");
                        break;
                    }
                case "tostring":
                    {
                        CheckCase("toString", function.Identifier.Name);
                        var length = function.TryThrowCount(1, 2);
                        var obj = Evaluate(function.Expressions[0]);
                        string format = length == 2 ? GetString(function, 1) : null;

                        if(obj is DateTime)
                        {
                            Result = ((DateTime)obj).ToString(format);
                        }
                        else if(this.IsNumber(obj))
                        {
                            Result = Convert.ToDouble(obj).ToString(format);
                        }
                        else
                        {
                            Result = Convert.ToString(obj);
                        }
                        break;
                    }
                #endregion

                #region 其他函数

                case "newguid":
                    {
                        CheckCase("newGuid", function.Identifier.Name);
                        var length = function.TryThrowCount(0, 1);
                        string format = length == 1 ? GetString(function, 0) : null;
                        Result = Guid.NewGuid().ToString(format);
                        break;
                    }

                case "isnull":
                    {
                        CheckCase("isnull", function.Identifier.Name);
                        var length = function.TryThrowCount(2);
                        var b = Evaluate(function.Expressions[0]);
                        if(b == null || Convert.IsDBNull(b))
                        {
                            Result = Evaluate(function.Expressions[1]);
                        }
                        else
                        {
                            Result = b;
                        }
                        break;
                    }
                #endregion

                #region if
                case "if":

                    CheckCase("if", function.Identifier.Name);

                    function.TryThrowCount(3);

                    bool cond = Convert.ToBoolean(Evaluate(function.Expressions[0]));

                    Result = cond ? Evaluate(function.Expressions[1]) : Evaluate(function.Expressions[2]);
                    break;

                #endregion

                #region in
                case "in":

                    CheckCase("in", function.Identifier.Name);

                    function.TryThrowMinCount(2);

                    object parameter = Evaluate(function.Expressions[0]);

                    bool evaluation = false;

                    // Goes through any values, and stop whe one is found
                    for(int i = 1 ; i < function.Expressions.Length ; i++)
                    {
                        object argument = Evaluate(function.Expressions[i]);
                        if(CompareUsingMostPreciseType(parameter, argument) == 0)
                        {
                            evaluation = true;
                            break;
                        }
                    }

                    Result = evaluation;
                    break;

                #endregion

                default:
                    throw new ArgumentException("未找到方法。",
                        function.Identifier.Name);
            }
        }
        private static readonly string[] NumChineseCharacter = new string[] { "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" };

        private string GetString(Function function, int index)
        {
            var o = Evaluate(function.Expressions[index]);
            if(o is string)
            {
                return (string)o;
            }
            else return Convert.ToString(o);
        }

        private int GetInteger(Function function, int index)
        {
            var o = Evaluate(function.Expressions[index]);
            if(this.IsNumber(o))
            {
                return Convert.ToInt32(o);
            }
            function.Throw("方法的第" + NumChineseCharacter[index] + "个参数仅允许为数字类型。");
            return 0;
        }

        private long GetLong(Function function, int index)
        {
            var o = Evaluate(function.Expressions[index]);
            if(this.IsNumber(o))
            {
                return Convert.ToInt64(o);
            }
            function.Throw("方法的第" + NumChineseCharacter[index] + "个参数仅允许为数字（Int64）类型。");
            return 0L;
        }

        private DateTime GetDate(Function function, int index)
        {
            var o = Evaluate(function.Expressions[index]);
            if(o is DateTime)
            {
                return (DateTime)o;
            }
            function.Throw("方法的第" + NumChineseCharacter[index] + "个参数仅允许为日期类型。");
            return DateTime.MinValue;
        }

        private double GetFloat(Function function, int index)
        {
            var o = Evaluate(function.Expressions[index]);
            if(this.IsNumber(o))
            {
                return Convert.ToDouble(o);
            }
            function.Throw("方法的第" + NumChineseCharacter[index] + "个参数仅允许为小数类型。");
            return 0;
        }

        private T EvaluateNumber<T>(Function function, int index)
        {
            var o = Evaluate(function.Expressions[index]);
            if(this.IsNumber(o)) return (T)o;

            function.Throw("");
            return default(T);
        }

        #endregion

        private void CheckCase(string function, string called)
        {
            if(function == called) return;
            if(IgnoreCase)
            {
                if(string.Equals(function, called, StringComparison.CurrentCultureIgnoreCase)) return;

                throw new ArgumentException("未找到方法。", called);
            }

            throw new ArgumentException(String.Format("未找到方法 {0}。当前模式下区分方法的大小写。", called, function));
        }

        public event EvaluateFunctionHandler EvaluateFunction;

        private void OnEvaluateFunction(string name, FunctionArgs args)
        {
            if(EvaluateFunction != null)
                EvaluateFunction(name, args);
        }

        public override void Visit(Identifier parameter)
        {
            if(Parameters.ContainsKey(parameter.Name))
            {
                // The parameter is defined in the hashtable
                if(Parameters[parameter.Name] is Expression)
                {
                    // The parameter is itself another Expression
                    var expression = (Expression)Parameters[parameter.Name];

                    // Overloads parameters 
                    foreach(var p in Parameters)
                    {
                        expression.Parameters[p.Key] = p.Value;
                    }

                    expression.EvaluateFunction += EvaluateFunction;
                    expression.EvaluateParameter += EvaluateParameter;

                    Result = ((Expression)Parameters[parameter.Name]).Evaluate();
                }
                else
                    Result = Parameters[parameter.Name];
            }
            else
            {
                // The parameter should be defined in a call back method
                var args = new ParameterArgs();

                if(parameter.Name == "null")
                {
                    Result = null;
                }
                else
                {
                    // Calls external implementation
                    OnEvaluateParameter(parameter.Name, args);

                    if(!args.HasResult)
                        throw new ArgumentException("参数未定义。", parameter.Name);

                    Result = args.Result;
                }
            }
        }

        public event EvaluateParameterHandler EvaluateParameter;

        private void OnEvaluateParameter(string name, ParameterArgs args)
        {
            if(EvaluateParameter != null)
                EvaluateParameter(name, args);
        }

        public Dictionary<string, object> Parameters { get; set; }
        public Dictionary<string, UserFunction> Functions { get; set; }

    }
}
